package com.stox.ui.javafx.charting.event.listener;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

import com.stox.dto.AbstractModel;
import com.stox.dto.AbstractModel.Type;
import com.stox.dto.IndicatorModel;
import com.stox.dto.intf.AbstractModelCollectionProvider;
import com.stox.event.AbstractModelSelectionEvent;
import com.stox.indicator.Indicator;
import com.stox.indicator.MACD;
import com.stox.indicator.Overlay;
import com.stox.indicator.VolumeByPrice;
import com.stox.ui.javafx.charting.model.plot.AbstractPlot;
import com.stox.ui.javafx.charting.view.ChartingView;
import com.stox.ui.javafx.charting.widget.Chart;
import com.stox.util.BeanUtil;

@Component
@SuppressWarnings("rawtypes")
public class IndicatorModelSelectionListener implements
		SmartApplicationListener {
	
	@Autowired
	private ApplicationContext applicationContext;

	@Override
	@SuppressWarnings({ "unchecked"})
	public void onApplicationEvent(ApplicationEvent event) {
		AbstractModelCollectionProvider<? extends Collection<AbstractModel>>
		abstractModelCollectionProvider = (AbstractModelCollectionProvider<? extends Collection<AbstractModel>>)
		event.getSource();

		Collection<AbstractModel> models = abstractModelCollectionProvider.get();
		models = getModelsOfType(models, IndicatorModel.INDICATOR_TYPE);
		if(!models.isEmpty() && null != ChartingView.getSelectedChartingView()){
			ChartingView view = ChartingView.getSelectedChartingView();
			for(AbstractModel model : models){
				if(existsModelOfType(model, view, IndicatorModel.INDICATOR_TYPE)) {
					continue;
				}
				
				IndicatorModel indicatorModel = (IndicatorModel)model;
				AbstractPlot abstractPlot = null;
				if(Overlay.class.isAssignableFrom(indicatorModel.getIndicator().getClass())){
					if(VolumeByPrice.class.isAssignableFrom(indicatorModel.getIndicator().getClass())){
						abstractPlot = (AbstractPlot)applicationContext.getBean("volumeByPriceOverlayPlot", model);
					}else{
						abstractPlot = (AbstractPlot)applicationContext.getBean("lineOverlayPlot", model);
					}
					view.getPrimaryChart().addPlot(abstractPlot);
				}else{
					Chart chart = null;
					if(MACD.class.isAssignableFrom(indicatorModel.getIndicator().getClass())){
						abstractPlot = (AbstractPlot)applicationContext.getBean("macdPlot", model);
					}else{
						chart = findChartByIndicator(indicatorModel.getIndicator(), view);
					}
					
					if(null == chart){
						chart = (Chart)applicationContext.getBean("secondaryChart", view);
						view.addSecondaryChart(chart);
					}
					
					if(null == abstractPlot){
						abstractPlot = (AbstractPlot)applicationContext.getBean("linePlot", model);
					}
					chart.setFrontPlot(abstractPlot);
				}
				abstractPlot.setConfigurationBeanDescriptor(BeanUtil.introspectBean(indicatorModel.getConfiguration(), true));
				view.update();
			}
		}
	}
	
	private Chart findChartByIndicator(Indicator indicator, ChartingView view){
		for(Chart chart : view.getSecondaryCharts()){
			AbstractPlot plot = chart.getFrontPlot();
			if(plot.getModel().getType().isSubTypeOf(IndicatorModel.INDICATOR_TYPE)){
				IndicatorModel indicatorModel = (IndicatorModel)plot.getModel();
				if(indicatorModel.getIndicator().equals(indicator)){
					return chart;
				}
			}
			for(Object object : chart.getPlots()){
				plot = (AbstractPlot)object;
				if(plot.getModel().getType().isSubTypeOf(IndicatorModel.INDICATOR_TYPE)){
					IndicatorModel indicatorModel = (IndicatorModel)plot.getModel();
					if(indicatorModel.getIndicator().equals(indicator)){
						return chart;
					}
				}
			}
		}
		return null;
	}
	
	private boolean existsModelOfType(AbstractModel model, ChartingView view, Type type){
		if(existsModelOfType(model, view.getPrimaryChart(), type)) {
			return true;
		}
		for(Chart chart : view.getSecondaryCharts()){
			if(existsModelOfType(model, chart, type)) {
				return true;
			}
		}
		return false;
	}
	
	private boolean existsModelOfType(AbstractModel model, Chart chart, Type type){
		AbstractPlot plot = chart.getFrontPlot();
		if(plot.getModel().getType().isSubTypeOf(type)){
			if(plot.getModel().equals(model)){
				return true;
			}
		}
		for(Object object : chart.getPlots()){
			plot = (AbstractPlot)object;
			if(plot.getModel().getType().isSubTypeOf(type)){
				if(plot.getModel().equals(model)){
					return true;
				}
			}
		}
		return false;
	}

	private Collection<AbstractModel> getModelsOfType(Collection<AbstractModel> models, Type type){
		Collection<AbstractModel> result = new ArrayList<AbstractModel>();
		for(AbstractModel abstractModel : models){
			if(abstractModel.getType().isSubTypeOf(type)){
				result.add(abstractModel);
			}
		}
		return result;
	}
	
	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
		return AbstractModelSelectionEvent.class.isAssignableFrom(eventType);
	}

	@Override
	public boolean supportsSourceType(Class<?> sourceType) {
		return true;
	}

}
